
#include <BLabel>
#include <BMenu>
#include <BDockLayout>
#include <BHBoxLayout>
#include <BPainter>
#include <BSystem>
#include <BListItem>
#include <BColorDialog>

#include "FontDialog.h"
#include "TextEditor.h"

TextEditor::TextEditor(BWidget* parent) : BTextEdit(parent)
{
	_clearButton = new BButton(Form_IconOnly);
	_clearButton->setSizePolicy(Policy_Preferred);

	_fontButton = new BButton(Form_IconOnly);
	_fontButton->setSizePolicy(Policy_Preferred);

	_colorButton = new BButton(Form_IconOnly);
	_colorButton->setSizePolicy(Policy_Preferred);

	_groundButton = new BButton(Graph_Square_Fill);
	_groundButton->setSizePolicy(Policy_Preferred);

	_viewButton = new BButton(u8"视图");
	_viewButton->setForm(Form_IconAlone);
	{
		BMenu* showPunctMenu = new BMenu();
		showPunctMenu->setName("view");
		showPunctMenu->addAction("show-tabs")->setCheckable(true);
		showPunctMenu->addAction("show-spaces")->setCheckable(true);
		showPunctMenu->addAction("show-returns")->setCheckable(true);
		showPunctMenu->addAction("show-linefeed")->setCheckable(true);
		_viewButton->setMenu(showPunctMenu);
		connect(showPunctMenu, Signal_Triggered, &TextEditor::slotShowPunctMenuTriggered);
	}

	BHBoxLayout* topLayout = new BHBoxLayout(this, Part_Top);
	topLayout->addSpring(1);
	topLayout->addWidget(_clearButton, 0);
	topLayout->addWidget(_fontButton, 0);
	topLayout->addWidget(_colorButton, 0);
	topLayout->addWidget(_groundButton, 0);
	topLayout->addSpring(1);
	topLayout->addWidget(_viewButton, 0);

	_formatDialog = new FormatDialog();
	//_formatDialog->setVisible(true);
	BDockLayout* dockLayout = new BDockLayout(this);
	dockLayout->addWidget(_formatDialog, Align_RightBottom);

	connect(this, Signal_AssetChanged, &TextEditor::slotAssetChanged);
	connect(this, Signal_Styled, &TextEditor::slotTextEditStyled);
	connect(this, Signal_Framed, &TextEditor::slotTextEditFramed);
	connect(this, Signal_SelectionChanged, &TextEditor::slotTextSelectionChanged);
	connect(this, Signal_Wheeled, &TextEditor::slotTextEditWheeled);

	connect(_clearButton, Signal_Clicked, &TextEditor::slotClearButtonClicked);
	connect(_fontButton, Signal_Clicked, &TextEditor::slotFontButtonClicked);
	connect(_colorButton, Signal_Clicked, &TextEditor::slotColorButtonClicked);

}
TextEditor::~TextEditor()
{

}

void TextEditor::slotAssetChanged(BWidget* widget, const BValue& value)
{
	if (BListItem* item = value)
	{
		this->setTextFormat(item->property("format"));
		_formatDialog->setAsset(item->property("format"));
	}
}

void TextEditor::slotTextEditStyled(BWidget* widget, const BValue& value)
{
	const BStyle* style = value;
	if (!widget->font())
	{
		BFont* font = new BFont(*style->font());
		widget->setFont(font);
	}
}
void TextEditor::slotTextEditFramed(BTextEdit* textEdit)
{
	BPainter painter(textEdit);

	BMenu* showPunctMenu = _viewButton->menu();
	bool show_tabs = showPunctMenu->action("show-tabs")->checked();
	bool show_spaces = showPunctMenu->action("show-spaces")->checked();
	bool show_returns = showPunctMenu->action("show-returns")->checked();
	bool show_linefeed = showPunctMenu->action("show-linefeed")->checked();

	BRange lineRange = textEdit->query("visual-range");
	if (lineRange.valid())
	{
		painter.setColor(200, 0, 0);
		for (int line = lineRange.min(); line < lineRange.max(); line++)
		{
			const BString& text = textEdit->lineText(line);
			if (text.empty())
				continue;
			for (auto it = text.begin(); it.valid(); ++it)
			{
				if (!it->isSpace())
					continue;
				BRectf rect = textEdit->codeRect(line, it.count());
				if (it == '\t' && show_tabs)
				{
					painter.drawLine(rect.leftCenter(), rect.rightCenter());
					float x = rect.right() - 5;
					float y0 = rect.center().y() - 5;
					float y1 = rect.center().y() + 5;
					painter.drawLine(BPointf(x, y0), rect.rightCenter());
					painter.drawLine(BPointf(x, y1), rect.rightCenter());
				}
				if (it == ' ' && show_spaces)
				{
					painter.setPointSmooth(true);
					painter.setPointSize(3);
					painter.drawPoint(rect.center());
					painter.setPointSize(1);
					painter.setPointSmooth(false);
				}
				if (it == '\r' && show_returns)
				{
					rect %= 60;
					painter.drawLine(rect.leftCenter(), rect.rightCenter());
					float x = rect.left() + 5;
					float y0 = rect.center().y() - 5;
					float y1 = rect.center().y() + 5;
					painter.drawLine(rect.leftCenter(), BPointf(x, y0));
					painter.drawLine(rect.leftCenter(), BPointf(x, y1));
					painter.drawLine(BPointf(rect.left(), y0), BPointf(rect.left(), y1));
				}
				if (it == '\n' && show_linefeed)
				{
					rect %= 60;
					painter.drawLine(rect.rightTop(), rect.rightBottom());
					painter.drawLine(rect.leftBottom(), rect.rightBottom());
					float x = rect.left() + 5;
					float y0 = rect.bottom() - 5;
					float y1 = rect.bottom() + 5;
					painter.drawLine(rect.leftBottom(), BPointf(x, y0));
					painter.drawLine(rect.leftBottom(), BPointf(x, y1));
				}
			}
		}
	}
}
void TextEditor::slotTextSelectionChanged(BWidget* widget, const BValue& value)
{
	BRange range = value;
	if (TextFormat* textFormat = (TextFormat*)this->textFormat())
	{
		const BFont* font = textFormat->font(range.min());
		if (const BColor* color = textFormat->color(range.min()))
		{
			_colorButton->setColor(*color);
		}
		if (const BColor* ground = textFormat->ground(range.min()))
		{
			_groundButton->setColor(*ground);
		}
	}
}
void TextEditor::slotTextEditWheeled(BWidget* widget, const BValue& value)
{
	if (BSystem::KeyState(Key_Ctrl))
	{
		int wheel = value;
		BFont* font = widget->font();
		int fontSize = font->size();
		if (wheel > 0)
			fontSize += 1;
		else
			fontSize -= 1;
		font->setSize(fontSize);
		this->setFont(font);
	}
}

void TextEditor::slotClearButtonClicked(BObject* object, const BValue& value)
{
	if (TextFormat* format = dynamic_cast<TextFormat*>(this->textFormat()))
	{
		format->clear();
		this->emit(Signal_Changed);
	}
}
void TextEditor::slotFontButtonClicked(BObject* object, const BValue& value)
{
	static FontDialog* fontDialog = new FontDialog(this);
	fontDialog->setAsset(object->asset());
	if (fontDialog->execute())
	{
		BFont* font = fontDialog->asset();
		BRange range = this->selectedRange();
		TextFormat* format = (TextFormat*)this->textFormat();
		format->setFont(range.min(), range.size(), font);
		object->setAsset(font);
		this->emit(Signal_Changed);
	}
}
void TextEditor::slotColorButtonClicked(BButton* button, const BValue& value)
{
	static BColorDialog* colorDialog = new BColorDialog(this);
	colorDialog->setColor(button->color());
	if (colorDialog->execute())
	{
		const BColor& color = colorDialog->color();
		BRange range = this->selectedRange();
		TextFormat* format = (TextFormat*)this->textFormat();
		format->setColor(range.min(), range.size(), color);
		button->setColor(color);
		this->emit(Signal_Changed);
	}
}
void TextEditor::slotShowPunctMenuTriggered(BMenu* menu, const BValue& value)
{
	BAction* action = value;
	this->setProperty(action->text(), action->checked());
	this->fresh();
}

void TextEditor::styleEvent(const BEvent& event)
{
	BTextEdit::styleEvent(event);
	BStyle* style = event.value();
	_clearButton->setIcon(style->icon("clear"));
	_fontButton->setImage(style->image("font"));
	_colorButton->setImage(style->font()->image('A'));
}
